listview: Try to keep the list items in order when scrolling
authorBenjamin Otte <otte@redhat.com>
Fri, 28 Sep 2018 01:33:16 +0000 (03:33 +0200)
committerMatthias Clasen <mclasen@redhat.com>
Sat, 30 May 2020 23:26:45 +0000 (19:26 -0400)
Instead of just destroying all items and then recreating them (or even
hide()ing and then show()ing them again (or even even repositioning
them in the widget tree)), just try to reust them in the order they are.

This works surprisingly well when scrolling and most/all widgets
just moved.

gtk/gtklistitemmanager.c
gtk/gtklistitemmanagerprivate.h
gtk/gtklistview.c

index 32687440b269733cf235aa465b257fc07e949665..62888f366ae490e59cbe17da4137c72ead541845 100644 (file)
@@ -21,6 +21,8 @@
 
 #include "gtklistitemmanagerprivate.h"
 
+#include "gtkwidgetprivate.h"
+
 struct _GtkListItemManager
 {
   GObject parent_instance;
@@ -312,6 +314,33 @@ gtk_list_item_manager_try_reacquire_list_item (GtkListItemManager       *self,
   return GTK_WIDGET (result);
 }
 
+/**
+ * gtk_list_item_manager_move_list_item:
+ * @self: a #GtkListItemManager
+ * @list_item: an acquired #GtkListItem that should be moved to represent
+ *     a different row
+ * @position: the new position of that list item
+ * @prev_sibling: the new previous sibling
+ *
+ * Moves the widget to represent a new position in the listmodel without
+ * releasing the item.
+ *
+ * This is most useful when scrolling.
+ **/
+void
+gtk_list_item_manager_move_list_item (GtkListItemManager     *self,
+                                      GtkWidget              *list_item,
+                                      guint                   position,
+                                      GtkWidget              *prev_sibling)
+{
+  gpointer item;
+
+  item = g_list_model_get_item (self->model, position);
+  gtk_list_item_factory_bind (self->factory, GTK_LIST_ITEM (list_item), position, item);
+  gtk_widget_insert_after (list_item, _gtk_widget_get_parent (list_item), prev_sibling);
+  g_object_unref (item);
+}
+
 /**
  * gtk_list_item_manager_update_list_item:
  * @self: a #GtkListItemManager
index 0f1d1bddd117e8beb60f763263a32ca42f2e201d..d34e346a21e430f2de903a2f4898dc8bb26cb88e 100644 (file)
@@ -69,6 +69,10 @@ GtkWidget *             gtk_list_item_manager_try_reacquire_list_item
 void                    gtk_list_item_manager_update_list_item  (GtkListItemManager     *self,
                                                                  GtkWidget              *item,
                                                                  guint                   position);
+void                    gtk_list_item_manager_move_list_item    (GtkListItemManager     *self,
+                                                                 GtkWidget              *list_item,
+                                                                 guint                   position,
+                                                                 GtkWidget              *prev_sibling);
 void                    gtk_list_item_manager_release_list_item (GtkListItemManager     *self,
                                                                  GtkListItemManagerChange *change,
                                                                  GtkWidget              *widget);
index c9cfc7c840dcfbf8507dffe419a6b61dcaeb308e..35cf3b6da3ecbbba0b0988388537634eb48d76c1 100644 (file)
@@ -336,7 +336,8 @@ gtk_list_view_merge_list_rows (GtkListView *self,
 }
 
 static void
-gtk_list_view_release_rows (GtkListView *self)
+gtk_list_view_release_rows (GtkListView *self,
+                            GQueue      *released)
 {
   ListRow *row, *prev, *next;
   guint i;
@@ -347,7 +348,7 @@ gtk_list_view_release_rows (GtkListView *self)
     {
       if (row->widget)
         {
-          gtk_list_item_manager_release_list_item (self->item_manager, NULL, row->widget);
+          g_queue_push_tail (released, row->widget);
           row->widget = NULL;
           i++;
           prev = gtk_rb_tree_node_get_previous (row);
@@ -379,7 +380,7 @@ gtk_list_view_release_rows (GtkListView *self)
   
   if (row->widget)
     {
-      gtk_list_item_manager_release_list_item (self->item_manager, NULL, row->widget);
+      g_queue_push_tail (released, row->widget);
       row->widget = NULL;
       prev = gtk_rb_tree_node_get_previous (row);
       if (prev && gtk_list_view_merge_list_rows (self, prev, row))
@@ -390,7 +391,7 @@ gtk_list_view_release_rows (GtkListView *self)
     {
       if (next->widget)
         {
-          gtk_list_item_manager_release_list_item (self->item_manager, NULL, next->widget);
+          g_queue_push_tail (released, next->widget);
           next->widget = NULL;
         }
       gtk_list_view_merge_list_rows (self, row, next);
@@ -404,9 +405,10 @@ gtk_list_view_ensure_rows (GtkListView              *self,
 {
   ListRow *row, *new_row;
   guint i, offset;
-  GtkWidget *insert_after;
+  GtkWidget *widget, *insert_after;
+  GQueue released = G_QUEUE_INIT;
 
-  gtk_list_view_release_rows (self);
+  gtk_list_view_release_rows (self, &released);
 
   row = gtk_list_view_get_row (self, self->anchor_start, &offset);
   if (offset > 0)
@@ -444,9 +446,20 @@ gtk_list_view_ensure_rows (GtkListView              *self,
             }
           if (new_row->widget == NULL)
             {
-              new_row->widget = gtk_list_item_manager_acquire_list_item (self->item_manager,
-                                                                         i,
-                                                                         insert_after);
+              new_row->widget = g_queue_pop_head (&released);
+              if (new_row->widget)
+                {
+                  gtk_list_item_manager_move_list_item (self->item_manager,
+                                                        new_row->widget,
+                                                        i,
+                                                        insert_after);
+                }
+              else
+                {
+                  new_row->widget = gtk_list_item_manager_acquire_list_item (self->item_manager,
+                                                                             i,
+                                                                             insert_after);
+                }
             }
         }
       else
@@ -456,6 +469,9 @@ gtk_list_view_ensure_rows (GtkListView              *self,
         }
       insert_after = new_row->widget;
     }
+
+  while ((widget = g_queue_pop_head (&released)))
+    gtk_list_item_manager_release_list_item (self->item_manager, NULL, widget);
 }
 
 static void